home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / linuxcon.000 / linuxcon / linuxconf-1.6 / netconf / exports.c < prev    next >
C/C++ Source or Header  |  1996-07-19  |  10KB  |  444 lines

  1. #include <string.h>
  2. #include <stdlib.h>
  3. #include "../misc/misc.h"
  4. #include "../xconf/xconf.h"
  5. #include "netconf.h"
  6. #include "netconf.m"
  7.  
  8. static NETCONF_HELP_FILE helpf ("exports");
  9. CONFIG_FILE f_exports (ETC_EXPORTS,helpf
  10.     ,CONFIGF_MANAGED|CONFIGF_OPTIONNAL|CONFIGF_PROBED);
  11.  
  12. /* #Specification: /etc/exports / format
  13.     The /etc/exports file separate lines for each portion of the
  14.     file system exported. The format of each line is
  15.     
  16.     #
  17.     mount_point client_host[(option)] [ client_host[(options) ...] 
  18.     #
  19. */
  20. class HOST_OPT: public ARRAY_OBJ{
  21. public:
  22.     char *host;
  23.     char root;    // Root access allowed
  24.     char link_relative;
  25.     char rw;
  26.     /*~PROTOBEG~ HOST_OPT */
  27. public:
  28.     HOST_OPT (const char *_host, const char *options);
  29.     void write (char *buf);
  30.     ~HOST_OPT (void);
  31.     /*~PROTOEND~ HOST_OPT */
  32. };
  33.  
  34. PUBLIC HOST_OPT::HOST_OPT (const char *_host, const char *options)
  35. {
  36.     host = NULL;
  37.     root = 0;
  38.     link_relative = 0;
  39.     rw = 0;
  40.     host = strdup(_host);
  41.     if (options != NULL){
  42.         while (1){
  43.             const char *start = options = str_skip (options);
  44.             if (*options == '\0'){
  45.                 break;
  46.             }else{
  47.                 while (*options > ' ' && *options != ',') options++;
  48.                 int len = (int)(options - start);
  49.                 if (strncmp(start,"rw",len)==0){
  50.                     rw = 1;
  51.                 }else if (strncmp(start,"ro",len)==0){
  52.                     rw = 0;
  53.                 }else if (strncmp(start,"no_root_squash",len)==0){
  54.                     root = 1;
  55.                 }else if (strncmp(start,"root_squash",len)==0){
  56.                     root = 0;
  57.                 }else if (strncmp(start,"link_relative",len)==0){
  58.                     link_relative = 1;
  59.                 }else if (strncmp(start,"link_absolute",len)==0){
  60.                     link_relative = 0;
  61.                 }else{
  62.                     fprintf (stderr,"Unknown option %s\n",start);
  63.                 }
  64.                 options = str_skip(options);
  65.                 if (*options == ',') options++;
  66.             }
  67.         }
  68.     }
  69. }
  70.  
  71. PUBLIC HOST_OPT::~HOST_OPT()
  72. {
  73.     free (host);
  74. }
  75.  
  76. static char exports_wopt (const char *str, char *buf, char sep)
  77. {
  78.     if (str[0] != '\0'){
  79.         int len = strlen(buf);
  80.         buf[len++] = sep;
  81.         sep = ',';
  82.         strcpy (buf+len,str);
  83.     }
  84.     return sep;
  85. }
  86.  
  87. /*
  88.     Format a host export specification like in /etc/exports
  89. */
  90. PUBLIC void HOST_OPT::write (char *buf)
  91. {
  92.     buf = strcpy (buf,host);
  93.     /* #Specification: /etc/exports / default values
  94.         When writing back the /etc/exports file, we try to avoid
  95.         the generation of default value for option. The idea is
  96.         to keep the file readable by avoiding long option name.
  97.  
  98.         We are making an exception for option ro/rw, because it
  99.         is terribly important and because it is a short one.
  100.     */
  101.     // The keyword associated with the default behavior is set to ""
  102.     // so we know we don't have to generate it.
  103.     char sep = '(';    
  104.     static char *tb_rw[]={"ro","rw"};
  105.     sep = exports_wopt (tb_rw[rw],buf,sep);
  106.     static char *tb_root[]={"","no_root_squash"};
  107.     sep = exports_wopt (tb_root[root],buf,sep);
  108.     static char *tb_link[]={"","link_relative"};
  109.     sep = exports_wopt (tb_link[link_relative],buf,sep);
  110.     if (sep == ',') strcat (buf,")");
  111.     strcat (buf," ");
  112. }
  113.  
  114. class HOSTS_OPT: public ARRAY{
  115.     /*~PROTOBEG~ HOSTS_OPT */
  116. public:
  117.     void add (const char *_host, const char *_option);
  118.     HOST_OPT *getitem (int no);
  119.     char *parse_add (const char *pt, int &err);
  120.     void write (FILE *fout);
  121.     /*~PROTOEND~ HOSTS_OPT */
  122. };
  123.  
  124. PUBLIC void HOSTS_OPT::add (const char *_host, const char *_option)
  125. {
  126.     HOST_OPT *opt = new HOST_OPT(_host,_option);
  127.     if (opt != NULL) ARRAY::add (opt);
  128. }
  129.  
  130. /*
  131.     Parse a string to extract a host(option) spec.
  132.     Return a pointer after the spec. If the spec is valid, it is
  133.     add to the array.
  134. */
  135. PUBLIC char *HOSTS_OPT::parse_add (const char *pt, int &err)
  136. {
  137.     char tmp[200];
  138.     char *dst = tmp;
  139.     while (*pt > ' ' && *pt != '(') *dst++ = *pt++;
  140.     *dst = '\0';
  141.     pt = str_skip(pt);
  142.     if (*pt == '('){
  143.         pt++;
  144.         if (dst == tmp){
  145.             // Host name empty ???
  146.             err = 1;
  147.         }else{
  148.             char opt[1000];
  149.             dst = opt;
  150.             while (*pt != '\0' && *pt != ')') *dst++ = *pt++;
  151.             *dst = '\0';
  152.             if (*pt == ')') pt++;
  153.             add (tmp,opt);
  154.         }
  155.     }else if (dst != tmp){
  156.         add (tmp,NULL);
  157.     }
  158.     return (char*)pt;
  159. }
  160.  
  161. PUBLIC HOST_OPT *HOSTS_OPT::getitem(int no)
  162. {
  163.     return (HOST_OPT *)ARRAY::getitem(no);
  164. }
  165.  
  166. PUBLIC void HOSTS_OPT::write (FILE *fout)
  167. {
  168.     for (int i=0; i<getnb(); i++){
  169.         char buf[200];
  170.         getitem(i)->write(buf);
  171.         fputs (buf,fout);
  172.     }
  173. }
  174.  
  175. class EXPORT_FS: public ARRAY_OBJ{
  176.     char *comment;
  177.     char *path;
  178.     HOSTS_OPT tbhost;
  179.     /*~PROTOBEG~ EXPORT_FS */
  180. public:
  181.     EXPORT_FS (const char *buf, int noline);
  182.     int edit (void);
  183.     const char *getpath (void);
  184.     void write (FILE *fout);
  185.     ~EXPORT_FS (void);
  186.     /*~PROTOEND~ EXPORT_FS */
  187. };
  188.  
  189. /*
  190.     Parse one line of /etc/exports
  191. */
  192. PUBLIC EXPORT_FS::EXPORT_FS (const char *buf, int noline)
  193. {
  194.     /* #Specification: /etc/exports / configurator
  195.         The configurator remember comments, either full line or
  196.         at the end of a line.
  197.  
  198.         It read it and write it back. This means that /etc/exports
  199.         may still be edited by hand or by netconf.
  200.     */
  201.     char *pt = str_skip(buf);
  202.     comment = NULL;
  203.     path = NULL;
  204.     if (*pt == '#'){
  205.         comment = strdup(str_skip(pt+1));
  206.     }else if (*pt != '\0'){
  207.         char tmp[1000];
  208.         pt = str_copyword (tmp,pt);
  209.         if (tmp[0] != '\0'){
  210.             path = strdup(tmp);
  211.             int err = 0;
  212.             while (1){
  213.                 pt = str_skip (pt);
  214.                 if (*pt == '#'){
  215.                     comment = strdup(str_skip(pt+1));
  216.                     break;
  217.                 }else if (*pt == '\0'){
  218.                     break;
  219.                 }else{
  220.                     pt = tbhost.parse_add (pt,err);
  221.                 }
  222.             }
  223.             if (err){
  224.                 xconf_error (MSG_U(E_IVLEXPORTS
  225.                     ,"Invalid line %d in file %s\n%s\n")
  226.                     ,noline,ETC_EXPORTS,buf);
  227.                 free (path);
  228.                 path = NULL;
  229.                 free (comment);
  230.                 comment = strdup(buf);
  231.             }
  232.         }
  233.     }
  234. }
  235.  
  236. PUBLIC EXPORT_FS::~EXPORT_FS ()
  237. {
  238.     free (comment);
  239.     free (path);
  240. }
  241.  
  242. PUBLIC const char *EXPORT_FS::getpath()
  243. {
  244.     return path == NULL ? "" : path;
  245. }
  246.  
  247. PUBLIC void EXPORT_FS::write (FILE *fout)
  248. {
  249.     if (path != NULL){
  250.         fprintf (fout,"%s ",path);
  251.         tbhost.write (fout);
  252.         fputc (' ',fout);    // Put a space for the comment in case.
  253.                             // Look nicer
  254.     }
  255.     if (comment != NULL){
  256.         fprintf (fout,"# %s",comment);
  257.     }
  258.     fputc ('\n',fout);
  259. }
  260.  
  261. /*
  262.     Edit the spec of one export directory
  263. */
  264. PUBLIC int EXPORT_FS::edit()
  265. {
  266.     const int NBFIELD = 7;
  267.     const int F_comment = NBFIELD-1;
  268.     const int F_path = 0;
  269.     char tb[NBFIELD][MAX_LEN+1];
  270.     memset (tb,'\0',sizeof(tb));
  271.     if (path != NULL) strcpy (tb[F_path],path);
  272.     if (comment != NULL) strcpy (tb[F_comment],comment);
  273.     /* Not very good because it assume a fixe number of host */
  274.     /* this part is only a prototype as the user interface part is */
  275.     /* not flexible enough */
  276.     for (int i=0; i<tbhost.getnb() && i < F_comment -1; i++){
  277.         tbhost.getitem(i)->write(tb[i+1]);
  278.     }
  279.     int nofield = 0;
  280.     int ret = -1;
  281.     while (1){
  282.         static const char *prompts[NBFIELD]={
  283.             MSG_U(F_PATHEXP,"Path to export"),
  284.             MSG_U(F_CLIHOSTS,"Client hosts + option"),
  285.             " ",
  286.             " ",
  287.             " ",
  288.             " ",
  289.             MSG_U(F_COMMENT,"Comment (opt)")
  290.         };
  291.         if (xconf_multiinp(MSG_U(T_ONEEXPORT,"One exported file system")
  292.             ,NULL
  293.             ,helpf
  294.             ,prompts,tb,NBFIELD,1,nofield)==MENU_ACCEPT){
  295.             // Some validation missing ...
  296.             tbhost.delall();
  297.             path = replaceif (path,tb[F_path]);
  298.             comment = replaceif (comment,tb[F_comment]);
  299.             int err = 0;
  300.             for (i=1; i<F_comment; i++){
  301.                 if (tb[i][0] != '\0'){
  302.                     int this_err=0;
  303.                     tbhost.parse_add (tb[i],this_err);
  304.                     if (this_err){
  305.                         nofield = i;
  306.                         err = 1;
  307.                     }
  308.                 }
  309.             }
  310.             if(!err){
  311.                 ret = 0;
  312.                 break;
  313.             }
  314.         }else{
  315.             break;
  316.         }
  317.     }
  318.     return ret;
  319. }
  320.  
  321. /*
  322.     EXPORTS handle the file /etc/exports.
  323.     Each line of this file is a EXPORT_FS (including comments).
  324. */
  325. class EXPORTS:     public ARRAY{
  326.     /*~PROTOBEG~ EXPORTS */
  327. public:
  328.     EXPORTS (void);
  329.     int edit (void);
  330.     EXPORT_FS *getitem (int no);
  331.     void write (void);
  332.     /*~PROTOEND~ EXPORTS */
  333. };
  334.  
  335. PUBLIC EXPORTS::EXPORTS()
  336. {
  337.     /* #Specification: /etc/exports / missing
  338.         A missing /etc/exports is equivalent to an empty one.
  339.     */
  340.     FILE *fin = f_exports.fopen ("r");
  341.     if (fin != NULL){
  342.         char buf[1000];
  343.         int noline = 0;
  344.         while (fgets_strip(buf,sizeof(buf)-1,fin,'\\',(char)255,&noline)!=NULL){
  345.             if (buf[0] != '\0'){
  346.                 add (new EXPORT_FS(buf,noline));
  347.             }
  348.         }
  349.         fclose (fin);
  350.     }
  351. }
  352.  
  353. PUBLIC EXPORT_FS *EXPORTS::getitem(int no)
  354. {
  355.     return (EXPORT_FS *)ARRAY::getitem(no);
  356. }
  357. PUBLIC void EXPORTS::write ()
  358. {
  359.     FILE *fout = f_exports.fopen ("w");
  360.     if (fout != NULL){
  361.         for (int i=0; i<getnb(); i++) getitem(i)->write(fout);
  362.         fclose (fout);
  363.     }
  364. }
  365.  
  366. /*
  367.     Edite /etc/exports, return -1 if some error or abort
  368. */
  369. PUBLIC int EXPORTS::edit()
  370. {
  371.     int ret = -1;
  372.     int choice=0;
  373.     while (1){
  374.         int nbp = getnb();
  375.         const char **menuopt = new const char *[(nbp)*2+1];
  376.         int *tblk = new int [nbp];
  377.         int ii=0;
  378.         for (int i=0; i<nbp; i++){
  379.             EXPORT_FS *fs = getitem(i);
  380.             const char *path = fs->getpath();
  381.             if (path[0] != '\0'){
  382.                 tblk[ii/2] = i;
  383.                 menuopt[ii++] = " ";
  384.                 menuopt[ii++] = (char*)path;
  385.             }
  386.         }
  387.         menuopt[ii] = NULL;
  388.         MENU_STATUS code = xconf_menu (
  389.             MSG_U(T_EXPORTED,"Exported file systems")
  390.             ,MSG_U(I_EXPORTED
  391.              ,"Setup file systems available for client hosts\n"
  392.               "Those systems will be accessible through NFS\n")
  393.             ,helpf
  394.             ,ETC_EXPORTS
  395.             ,NULL
  396.             ,MSG_U(F_DELETEONE,"to delete one definition")
  397.             ,MSG_U(F_ADDONE,"to add a new definition")
  398.             ,menuopt,choice);
  399.         free (menuopt);
  400.         if (code == MENU_ESCAPE || code == MENU_QUIT){
  401.             break;
  402.         }else if (code == MENU_ADD){
  403.             EXPORT_FS *fs = new EXPORT_FS("",0);
  404.             if (fs != NULL && fs->edit()!=-1){
  405.                 add (fs);
  406.                 write();
  407.             }else{
  408.                 delete fs;
  409.             }
  410.         }else{
  411.             EXPORT_FS *fs = getitem(tblk[choice]);
  412.             if (code == MENU_DEL){
  413.                 remove (fs);
  414.                 delete fs;
  415.             }else{
  416.                 // Edit one hosts
  417.                 if (fs->edit() != -1) write();
  418.             }
  419.         }
  420.         free (tblk);
  421.     }
  422.     return ret;
  423. }
  424.  
  425. /*
  426.     Edit the file /etc/exports
  427. */
  428. void exports_edit()
  429. {
  430.     EXPORTS exp;
  431.     exp.edit();
  432. }
  433.  
  434.  
  435. #ifdef TEST
  436.  
  437. int main(int argc, char *argv[])
  438. {
  439.     exports_edit();
  440. }
  441.  
  442. #endif
  443.  
  444.